/*
 * Copyright © 2002 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * California 95054, U.S.A. All rights reserved.  Sun Microsystems, Inc. has
 * intellectual property rights relating to technology embodied in the product
 * that is described in this document. In particular, and without limitation,
 * these intellectual property rights may include one or more of the U.S.
 * patents listed at http://www.sun.com/patents and one or more additional
 * patents or pending patent applications in the U.S. and in other countries.
 * U.S. Government Rights - Commercial software. Government users are subject
 * to the Sun Microsystems, Inc. standard license agreement and applicable
 * provisions of the FAR and its supplements.  Use is subject to license terms.
 * Sun,  Sun Microsystems,  the Sun logo and  Java are trademarks or registered
 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries.  This
 * product is covered and controlled by U.S. Export Control laws and may be
 * subject to the export or import laws in other countries.  Nuclear, missile,
 * chemical biological weapons or nuclear maritime end uses or end users,
 * whether direct or indirect, are strictly prohibited.  Export or reexport
 * to countries subject to U.S. embargo or to entities identified on U.S.
 * export exclusion lists, including, but not limited to, the denied persons
 * and specially designated nationals lists is strictly prohibited.
 */

package org.javacc.parser;

import java.util.*;

final class KindInfo {
	long[] validKinds;

	long[] finalKinds;

	int validKindCnt = 0;

	int finalKindCnt = 0;

	KindInfo(int maxKind) {
		validKinds = new long[maxKind / 64 + 1];
		finalKinds = new long[maxKind / 64 + 1];
	}

	public void InsertValidKind(int kind) {
		validKinds[kind / 64] |= (1L << (kind % 64));
		validKindCnt++;
	}

	public void InsertFinalKind(int kind) {
		finalKinds[kind / 64] |= (1L << (kind % 64));
		finalKindCnt++;
	}
};

/**
 * Describes string literals.
 */

public class RStringLiteral extends RegularExpression {

	/**
	 * The string image of the literal.
	 */
	public String image;

	static int maxStrKind = 0;

	static int maxLen = 0;

	static int charCnt = 0;

	static Vector charPosKind = new Vector(); // Elements are hashtables

	// with single char keys;
	static int[] maxLenForActive = new int[100]; // 6400 tokens

	public static String[] allImages;

	static int[][] intermediateKinds;

	static int[][] intermediateMatchedPos;

	static int startStateCnt = 0;

	static boolean subString[];

	static boolean subStringAtPos[];

	static Hashtable[] statesForPos;

	// Need to call this method after gnerating code for each lexical state. It
	// initializes all the static variables, so that there is no interference
	// between the various states of the lexer.
	public static void ReInit() {
		maxStrKind = 0;
		maxLen = 0;
		charPosKind = new Vector();
		maxLenForActive = new int[100]; // 6400 tokens
		intermediateKinds = null;
		intermediateMatchedPos = null;
		startStateCnt = 0;
		subString = null;
		subStringAtPos = null;
		statesForPos = null;
	}

	public static void DumpStrLiteralImages(java.io.PrintWriter ostr) {
		String image;
		int charCnt = 0, i;

		ostr.println("public static final String[] jjstrLiteralImages = {");

		if (allImages == null || allImages.length == 0) {
			ostr.println("};");
			return;
		}

		allImages[0] = "";
		for (i = 0; i < allImages.length; i++) {
			if ((image = allImages[i]) == null
					|| ((LexGen.toSkip[i / 64] & (1L << (i % 64))) == 0L
							&& (LexGen.toMore[i / 64] & (1L << (i % 64))) == 0L && (LexGen.toToken[i / 64] & (1L << (i % 64))) == 0L)
					|| (LexGen.toSkip[i / 64] & (1L << (i % 64))) != 0L
					|| (LexGen.toMore[i / 64] & (1L << (i % 64))) != 0L
					|| LexGen.canReachOnMore[LexGen.lexStates[i]]
					|| ((Options.getIgnoreCase() || LexGen.ignoreCase[i]) && (!image
							.equals(image.toLowerCase()) || !image.equals(image
							.toUpperCase())))) {
				allImages[i] = null;
				if ((charCnt += 6) > 80) {
					ostr.println("");
					charCnt = 0;
				}

				ostr.print("null, ");
				continue;
			}

			String toPrint = "\"";

			for (int j = 0; j < image.length(); j++) {
				if (image.charAt(j) <= 0xff)
					toPrint += ("\\" + Integer.toOctalString((int) image
							.charAt(j)));
				else {
					String hexVal = Integer.toHexString((int) image.charAt(j));

					if (hexVal.length() == 3)
						hexVal = "0" + hexVal;
					toPrint += ("\\u" + hexVal);
				}
			}

			toPrint += ("\", ");

			if ((charCnt += toPrint.length()) >= 80) {
				ostr.println("");
				charCnt = 0;
			}

			ostr.print(toPrint);
		}

		while (++i < LexGen.maxOrdinal) {
			if ((charCnt += 6) > 80) {
				ostr.println("");
				charCnt = 0;
			}

			ostr.print("null, ");
			continue;
		}

		ostr.println("};");
	}

	// Used for top level string literals
	public void GenerateDfa(java.io.PrintWriter ostr, int kind) {
		String s;
		Hashtable temp;
		KindInfo info;
		int len;

		if (maxStrKind <= ordinal)
			maxStrKind = ordinal + 1;

		if ((len = image.length()) > maxLen)
			maxLen = len;

		char c;
		for (int i = 0; i < len; i++) {
			if (Options.getIgnoreCase())
				s = ("" + (c = image.charAt(i))).toLowerCase();
			else
				s = "" + (c = image.charAt(i));

			if (!NfaState.unicodeWarningGiven && c > 0xff
					&& !Options.getJavaUnicodeEscape()
					&& !Options.getUserCharStream()) {
				NfaState.unicodeWarningGiven = true;
				JavaCCErrors
						.warning(
								LexGen.curRE,
								"Non-ASCII characters used in regular expression."
										+ "Please make sure you use the correct Reader when you create the parser that can handle your character set.");
			}

			if (i >= charPosKind.size()) // Kludge, but OK
				charPosKind.addElement(temp = new Hashtable());
			else
				temp = (Hashtable) charPosKind.elementAt(i);

			if ((info = (KindInfo) temp.get(s)) == null)
				temp.put(s, info = new KindInfo(LexGen.maxOrdinal));

			if (i + 1 == len)
				info.InsertFinalKind(ordinal);
			else
				info.InsertValidKind(ordinal);

			if (!Options.getIgnoreCase() && LexGen.ignoreCase[ordinal]
					&& c != Character.toLowerCase(c)) {
				s = ("" + image.charAt(i)).toLowerCase();

				if (i >= charPosKind.size()) // Kludge, but OK
					charPosKind.addElement(temp = new Hashtable());
				else
					temp = (Hashtable) charPosKind.elementAt(i);

				if ((info = (KindInfo) temp.get(s)) == null)
					temp.put(s, info = new KindInfo(LexGen.maxOrdinal));

				if (i + 1 == len)
					info.InsertFinalKind(ordinal);
				else
					info.InsertValidKind(ordinal);
			}

			if (!Options.getIgnoreCase() && LexGen.ignoreCase[ordinal]
					&& c != Character.toUpperCase(c)) {
				s = ("" + image.charAt(i)).toUpperCase();

				if (i >= charPosKind.size()) // Kludge, but OK
					charPosKind.addElement(temp = new Hashtable());
				else
					temp = (Hashtable) charPosKind.elementAt(i);

				if ((info = (KindInfo) temp.get(s)) == null)
					temp.put(s, info = new KindInfo(LexGen.maxOrdinal));

				if (i + 1 == len)
					info.InsertFinalKind(ordinal);
				else
					info.InsertValidKind(ordinal);
			}
		}

		maxLenForActive[ordinal / 64] = Math.max(maxLenForActive[ordinal / 64],
				len - 1);
		allImages[ordinal] = image;
	}

	public Nfa GenerateNfa(boolean ignoreCase) {
		if (image.length() == 1) {
			RCharacterList temp = new RCharacterList(image.charAt(0));
			return temp.GenerateNfa(ignoreCase);
		}

		NfaState startState = new NfaState();
		NfaState theStartState = startState;
		NfaState finalState = null;

		if (image.length() == 0)
			return new Nfa(theStartState, theStartState);

		int i;

		for (i = 0; i < image.length(); i++) {
			finalState = new NfaState();
			startState.charMoves = new char[1];
			startState.AddChar(image.charAt(i));

			if (Options.getIgnoreCase() || ignoreCase) {
				startState.AddChar(Character.toLowerCase(image.charAt(i)));
				startState.AddChar(Character.toUpperCase(image.charAt(i)));
			}

			startState.next = finalState;
			startState = finalState;
		}

		return new Nfa(theStartState, finalState);
	}

	static void DumpNullStrLiterals(java.io.PrintWriter ostr) {
		ostr.println("{");

		if (NfaState.generatedStates != 0)
			ostr.println("   return jjMoveNfa" + LexGen.lexStateSuffix + "("
					+ NfaState.InitStateName() + ", 0);");
		else
			ostr.println("   return 1;");

		ostr.println("}");
	}

	private static int GetStateSetForKind(int pos, int kind) {
		if (LexGen.mixed[LexGen.lexStateIndex] || NfaState.generatedStates == 0)
			return -1;

		Hashtable allStateSets = statesForPos[pos];

		if (allStateSets == null)
			return -1;

		Enumeration e = allStateSets.keys();

		while (e.hasMoreElements()) {
			String s = (String) e.nextElement();
			long[] actives = (long[]) allStateSets.get(s);

			s = s.substring(s.indexOf(", ") + 2);
			s = s.substring(s.indexOf(", ") + 2);

			if (s.equals("null;"))
				continue;

			if (actives != null
					&& (actives[kind / 64] & (1L << (kind % 64))) != 0L) {
				return NfaState.AddStartStateSet(s);
			}
		}

		return -1;
	}

	static String GetLabel(int kind) {
		RegularExpression re = LexGen.rexprs[kind];

		if (re instanceof RStringLiteral)
			return " \""
					+ JavaCCGlobals.add_escapes(((RStringLiteral) re).image)
					+ "\"";
		else if (!re.label.equals(""))
			return " <" + re.label + ">";
		else
			return " <token of kind " + kind + ">";
	}

	static int GetLine(int kind) {
		return LexGen.rexprs[kind].line;
	}

	static int GetColumn(int kind) {
		return LexGen.rexprs[kind].column;
	}

	/**
	 * Returns true if s1 starts with s2 (ignoring case for each character).
	 */
	static private boolean StartsWithIgnoreCase(String s1, String s2) {
		if (s1.length() < s2.length())
			return false;

		for (int i = 0; i < s2.length(); i++) {
			char c1 = s1.charAt(i), c2 = s2.charAt(i);

			if (c1 != c2 && Character.toLowerCase(c2) != c1
					&& Character.toUpperCase(c2) != c1)
				return false;
		}

		return true;
	}

	static void FillSubString() {
		String image;
		subString = new boolean[maxStrKind + 1];
		subStringAtPos = new boolean[maxLen];

		for (int i = 0; i < maxStrKind; i++) {
			subString[i] = false;

			if ((image = allImages[i]) == null
					|| LexGen.lexStates[i] != LexGen.lexStateIndex)
				continue;

			if (LexGen.mixed[LexGen.lexStateIndex]) {
				// We will not optimize for mixed case
				subString[i] = true;
				subStringAtPos[image.length() - 1] = true;
				continue;
			}

			for (int j = 0; j < maxStrKind; j++) {
				if (j != i && LexGen.lexStates[j] == LexGen.lexStateIndex
						&& ((String) allImages[j]) != null) {
					if (((String) allImages[j]).indexOf(image) == 0) {
						subString[i] = true;
						subStringAtPos[image.length() - 1] = true;
						break;
					} else if (Options.getIgnoreCase()
							&& StartsWithIgnoreCase((String) allImages[j],
									image)) {
						subString[i] = true;
						subStringAtPos[image.length() - 1] = true;
						break;
					}
				}
			}
		}
	}

	static void DumpStartWithStates(java.io.PrintWriter ostr) {
		ostr.println((Options.getStatic() ? "static " : "")
				+ "private final int " + "jjStartNfaWithStates"
				+ LexGen.lexStateSuffix + "(int pos, int kind, int state)");
		ostr.println("{");
		ostr.println("   jjmatchedKind = kind;");
		ostr.println("   jjmatchedPos = pos;");

		if (Options.getDebugTokenManager()) {
			ostr
					.println("   debugStream.println(\"   No more string literal token matches are possible.\");");
			ostr
					.println("   debugStream.println(\"   Currently matched the first \" + (jjmatchedPos + 1) + \" characters as a \" + tokenImage[jjmatchedKind] + \" token.\");");
		}

		ostr.println("   try { curChar = input_stream.readChar(); }");
		ostr.println("   catch(java.io.IOException e) { return pos + 1; }");

		if (Options.getDebugTokenManager())
			ostr
					.println("   debugStream.println("
							+ (LexGen.maxLexStates > 1 ? "\"<\" + lexStateNames[curLexState] + \">\" + "
									: "")
							+ "\"Current character : \" + "
							+ "TokenMgrError.addEscapes(String.valueOf(curChar)) + \" (\" + (int)curChar + \") at line \" + input_stream.getLine() + \" column \" + input_stream.getColumn());");

		ostr.println("   return jjMoveNfa" + LexGen.lexStateSuffix
				+ "(state, pos + 1);");
		ostr.println("}");
	}

	private static boolean boilerPlateDumped = false;

	static void DumpBoilerPlate(java.io.PrintWriter ostr) {
		ostr.println((Options.getStatic() ? "static " : "")
				+ "private final int " + "jjStopAtPos(int pos, int kind)");
		ostr.println("{");
		ostr.println("   jjmatchedKind = kind;");
		ostr.println("   jjmatchedPos = pos;");

		if (Options.getDebugTokenManager()) {
			ostr
					.println("   debugStream.println(\"   No more string literal token matches are possible.\");");
			ostr
					.println("   debugStream.println(\"   Currently matched the first \" + (jjmatchedPos + 1) + \" characters as a \" + tokenImage[jjmatchedKind] + \" token.\");");
		}

		ostr.println("   return pos + 1;");
		ostr.println("}");
	}

	static String[] ReArrange(Hashtable tab) {
		String[] ret = new String[tab.size()];
		Enumeration e = tab.keys();
		int cnt = 0;

		while (e.hasMoreElements()) {
			int i = 0, j;
			String s;
			char c = (s = (String) e.nextElement()).charAt(0);

			while (i < cnt && ret[i].charAt(0) < c)
				i++;

			if (i < cnt)
				for (j = cnt - 1; j >= i; j--)
					ret[j + 1] = ret[j];

			ret[i] = s;
			cnt++;
		}

		return ret;
	}

	static void DumpDfaCode(java.io.PrintWriter ostr) {
		Hashtable tab;
		String key;
		KindInfo info;
		int maxLongsReqd = maxStrKind / 64 + 1;
		int i, j, k;
		boolean ifGenerated;
		LexGen.maxLongsReqd[LexGen.lexStateIndex] = maxLongsReqd;

		if (maxLen == 0) {
			ostr.println((Options.getStatic() ? "static " : "")
					+ "private final int " + "jjMoveStringLiteralDfa0"
					+ LexGen.lexStateSuffix + "()");

			DumpNullStrLiterals(ostr);
			return;
		}

		if (!boilerPlateDumped) {
			DumpBoilerPlate(ostr);
			boilerPlateDumped = true;
		}

		if (!LexGen.mixed[LexGen.lexStateIndex]
				&& NfaState.generatedStates != 0)
			DumpStartWithStates(ostr);

		boolean startNfaNeeded;
		for (i = 0; i < maxLen; i++) {
			boolean atLeastOne = false;
			startNfaNeeded = false;
			tab = (Hashtable) charPosKind.elementAt(i);
			String[] keys = ReArrange(tab);

			ostr.print((Options.getStatic() ? "static " : "")
					+ "private final int " + "jjMoveStringLiteralDfa" + i
					+ LexGen.lexStateSuffix + "(");

			if (i != 0) {
				if (i == 1) {
					for (j = 0; j < maxLongsReqd - 1; j++)
						if (i <= maxLenForActive[j]) {
							if (atLeastOne)
								ostr.print(", ");
							else
								atLeastOne = true;
							ostr.print("long active" + j);
						}

					if (i <= maxLenForActive[j]) {
						if (atLeastOne)
							ostr.print(", ");
						ostr.print("long active" + j);
					}
				} else {
					for (j = 0; j < maxLongsReqd - 1; j++)
						if (i <= maxLenForActive[j] + 1) {
							if (atLeastOne)
								ostr.print(", ");
							else
								atLeastOne = true;
							ostr.print("long old" + j + ", long active" + j);
						}

					if (i <= maxLenForActive[j] + 1) {
						if (atLeastOne)
							ostr.print(", ");
						ostr.print("long old" + j + ", long active" + j);
					}
				}
			}
			ostr.println(")");
			ostr.println("{");

			if (i != 0) {
				if (i > 1) {
					atLeastOne = false;
					ostr.print("   if ((");

					for (j = 0; j < maxLongsReqd - 1; j++)
						if (i <= maxLenForActive[j] + 1) {
							if (atLeastOne)
								ostr.print(" | ");
							else
								atLeastOne = true;
							ostr.print("(active" + j + " &= old" + j + ")");
						}

					if (i <= maxLenForActive[j] + 1) {
						if (atLeastOne)
							ostr.print(" | ");
						ostr.print("(active" + j + " &= old" + j + ")");
					}

					ostr.println(") == 0L)");
					if (!LexGen.mixed[LexGen.lexStateIndex]
							&& NfaState.generatedStates != 0) {
						ostr.print("      return jjStartNfa"
								+ LexGen.lexStateSuffix + "(" + (i - 2) + ", ");
						for (j = 0; j < maxLongsReqd - 1; j++)
							if (i <= maxLenForActive[j] + 1)
								ostr.print("old" + j + ", ");
							else
								ostr.print("0L, ");
						if (i <= maxLenForActive[j] + 1)
							ostr.println("old" + j + "); ");
						else
							ostr.println("0L);");
					} else if (NfaState.generatedStates != 0)
						ostr.println("      return jjMoveNfa"
								+ LexGen.lexStateSuffix + "("
								+ NfaState.InitStateName() + ", " + (i - 1)
								+ ");");
					else
						ostr.println("      return " + i + ";");
				}

				if (i != 0 && Options.getDebugTokenManager()) {
					ostr
							.println("   if (jjmatchedKind != 0 && jjmatchedKind != 0x"
									+ Integer.toHexString(Integer.MAX_VALUE)
									+ ")");
					ostr
							.println("      debugStream.println(\"   Currently matched the first \" + (jjmatchedPos + 1) + \" characters as a \" + tokenImage[jjmatchedKind] + \" token.\");");

					ostr
							.println("   debugStream.println(\"   Possible string literal matches : { \"");

					for (int vecs = 0; vecs < maxStrKind / 64 + 1; vecs++) {
						if (i <= maxLenForActive[vecs]) {
							ostr.println(" + ");
							ostr.print("         jjKindsForBitVector(" + vecs
									+ ", ");
							ostr.print("active" + vecs + ") ");
						}
					}

					ostr.println(" + \" } \");");
				}

				ostr.println("   try { curChar = input_stream.readChar(); }");
				ostr.println("   catch(java.io.IOException e) {");

				if (!LexGen.mixed[LexGen.lexStateIndex]
						&& NfaState.generatedStates != 0) {
					ostr.print("      jjStopStringLiteralDfa"
							+ LexGen.lexStateSuffix + "(" + (i - 1) + ", ");
					for (k = 0; k < maxLongsReqd - 1; k++)
						if (i <= maxLenForActive[k])
							ostr.print("active" + k + ", ");
						else
							ostr.print("0L, ");

					if (i <= maxLenForActive[k])
						ostr.println("active" + k + ");");
					else
						ostr.println("0L);");

					if (i != 0 && Options.getDebugTokenManager()) {
						ostr
								.println("      if (jjmatchedKind != 0 && jjmatchedKind != 0x"
										+ Integer
												.toHexString(Integer.MAX_VALUE)
										+ ")");
						ostr
								.println("         debugStream.println(\"   Currently matched the first \" + (jjmatchedPos + 1) + \" characters as a \" + tokenImage[jjmatchedKind] + \" token.\");");
					}
					ostr.println("      return " + i + ";");
				} else if (NfaState.generatedStates != 0)
					ostr.println("   return jjMoveNfa" + LexGen.lexStateSuffix
							+ "(" + NfaState.InitStateName() + ", " + (i - 1)
							+ ");");
				else
					ostr.println("      return " + i + ";");

				ostr.println("   }");
			}

			if (i != 0 && Options.getDebugTokenManager())
				ostr
						.println("   debugStream.println("
								+ (LexGen.maxLexStates > 1 ? "\"<\" + lexStateNames[curLexState] + \">\" + "
										: "")
								+ "\"Current character : \" + "
								+ "TokenMgrError.addEscapes(String.valueOf(curChar)) + \" (\" + (int)curChar + \") at line \" + input_stream.getLine() + \" column \" + input_stream.getColumn());");

			ostr.println("   switch(curChar)");
			ostr.println("   {");

			CaseLoop: for (int q = 0; q < keys.length; q++) {
				key = keys[q];
				info = (KindInfo) tab.get(key);
				ifGenerated = false;
				char c = key.charAt(0);

				if (i == 0
						&& c < 128
						&& info.finalKindCnt != 0
						&& (NfaState.generatedStates == 0 || !NfaState
								.CanStartNfaUsingAscii(c))) {
					int kind;
					Outer: for (j = 0; j < maxLongsReqd; j++)
						if (info.finalKinds[j] != 0L)
							break;

					for (k = 0; k < 64; k++)
						if ((info.finalKinds[j] & (1L << k)) != 0L
								&& !subString[kind = (j * 64 + k)]) {
							if ((intermediateKinds != null
									&& intermediateKinds[(j * 64 + k)] != null
									&& intermediateKinds[(j * 64 + k)][i] < (j * 64 + k)
									&& intermediateMatchedPos != null && intermediateMatchedPos[(j * 64 + k)][i] == i)
									|| (LexGen.canMatchAnyChar[LexGen.lexStateIndex] >= 0 && LexGen.canMatchAnyChar[LexGen.lexStateIndex] < (j * 64 + k)))
								break;
							else if ((LexGen.toSkip[kind / 64] & (1L << (kind % 64))) != 0L
									&& (LexGen.toSpecial[kind / 64] & (1L << (kind % 64))) == 0L
									&& LexGen.actions[kind] == null
									&& LexGen.newLexState[kind] == null) {
								LexGen.AddCharToSkip(c, kind);

								if (Options.getIgnoreCase()) {
									if (c != Character.toUpperCase(c))
										LexGen.AddCharToSkip(Character
												.toUpperCase(c), kind);

									if (c != Character.toLowerCase(c))
										LexGen.AddCharToSkip(Character
												.toLowerCase(c), kind);
								}
								continue CaseLoop;
							}
						}
				}

				// Since we know key is a single character ...
				if (Options.getIgnoreCase()) {
					if (c != Character.toUpperCase(c))
						ostr.println("      case "
								+ (int) Character.toUpperCase(c) + ":");

					if (c != Character.toLowerCase(c))
						ostr.println("      case "
								+ (int) Character.toLowerCase(c) + ":");
				}

				ostr.println("      case " + (int) c + ":");

				long matchedKind;
				String prefix = (i == 0) ? "         " : "            ";

				if (info.finalKindCnt != 0) {
					for (j = 0; j < maxLongsReqd; j++) {
						if ((matchedKind = info.finalKinds[j]) == 0L)
							continue;

						for (k = 0; k < 64; k++) {
							if ((matchedKind & (1L << k)) == 0L)
								continue;

							if (ifGenerated) {
								ostr.print("         else if ");
							} else if (i != 0)
								ostr.print("         if ");

							ifGenerated = true;

							int kindToPrint;
							if (i != 0) {
								ostr.println("((active" + j + " & 0x"
										+ Long.toHexString(1L << k)
										+ "L) != 0L)");
							}

							if (intermediateKinds != null
									&& intermediateKinds[(j * 64 + k)] != null
									&& intermediateKinds[(j * 64 + k)][i] < (j * 64 + k)
									&& intermediateMatchedPos != null
									&& intermediateMatchedPos[(j * 64 + k)][i] == i) {
								JavaCCErrors
										.warning(" \""
												+ JavaCCGlobals
														.add_escapes(allImages[j
																* 64 + k])
												+ "\" cannot be matched as a string literal token "
												+ "at line "
												+ GetLine(j * 64 + k)
												+ ", column "
												+ GetColumn(j * 64 + k)
												+ ". It will be matched as "
												+ GetLabel(intermediateKinds[(j * 64 + k)][i])
												+ ".");
								kindToPrint = intermediateKinds[(j * 64 + k)][i];
							} else if (i == 0
									&& LexGen.canMatchAnyChar[LexGen.lexStateIndex] >= 0
									&& LexGen.canMatchAnyChar[LexGen.lexStateIndex] < (j * 64 + k)) {
								JavaCCErrors
										.warning(" \""
												+ JavaCCGlobals
														.add_escapes(allImages[j
																* 64 + k])
												+ "\" cannot be matched as a string literal token "
												+ "at line "
												+ GetLine(j * 64 + k)
												+ ", column "
												+ GetColumn(j * 64 + k)
												+ ". It will be matched as "
												+ GetLabel(LexGen.canMatchAnyChar[LexGen.lexStateIndex])
												+ ".");
								kindToPrint = LexGen.canMatchAnyChar[LexGen.lexStateIndex];
							} else
								kindToPrint = j * 64 + k;

							if (!subString[(j * 64 + k)]) {
								int stateSetName = GetStateSetForKind(i, j * 64
										+ k);

								if (stateSetName != -1) {
									ostr.println(prefix
											+ "return jjStartNfaWithStates"
											+ LexGen.lexStateSuffix + "(" + i
											+ ", " + kindToPrint + ", "
											+ stateSetName + ");");
								} else
									ostr.println(prefix + "return jjStopAtPos"
											+ "(" + i + ", " + kindToPrint
											+ ");");
							} else {
								if ((LexGen.initMatch[LexGen.lexStateIndex] != 0 && LexGen.initMatch[LexGen.lexStateIndex] != Integer.MAX_VALUE)
										|| i != 0) {
									ostr.println("         {");
									ostr.println(prefix + "jjmatchedKind = "
											+ kindToPrint + ";");
									ostr.println(prefix + "jjmatchedPos = " + i
											+ ";");
									ostr.println("         }");
								} else
									ostr.println(prefix + "jjmatchedKind = "
											+ kindToPrint + ";");
							}
						}
					}
				}

				if (info.validKindCnt != 0) {
					atLeastOne = false;

					if (i == 0) {
						ostr.print("         return ");

						ostr.print("jjMoveStringLiteralDfa" + (i + 1)
								+ LexGen.lexStateSuffix + "(");
						for (j = 0; j < maxLongsReqd - 1; j++)
							if ((i + 1) <= maxLenForActive[j]) {
								if (atLeastOne)
									ostr.print(", ");
								else
									atLeastOne = true;

								ostr.print("0x"
										+ Long.toHexString(info.validKinds[j])
										+ "L");
							}

						if ((i + 1) <= maxLenForActive[j]) {
							if (atLeastOne)
								ostr.print(", ");

							ostr.print("0x"
									+ Long.toHexString(info.validKinds[j])
									+ "L");
						}
						ostr.println(");");
					} else {
						ostr.print("         return ");

						ostr.print("jjMoveStringLiteralDfa" + (i + 1)
								+ LexGen.lexStateSuffix + "(");

						for (j = 0; j < maxLongsReqd - 1; j++)
							if ((i + 1) <= maxLenForActive[j] + 1) {
								if (atLeastOne)
									ostr.print(", ");
								else
									atLeastOne = true;

								if (info.validKinds[j] != 0L)
									ostr
											.print("active"
													+ j
													+ ", 0x"
													+ Long
															.toHexString(info.validKinds[j])
													+ "L");
								else
									ostr.print("active" + j + ", 0L");
							}

						if ((i + 1) <= maxLenForActive[j] + 1) {
							if (atLeastOne)
								ostr.print(", ");
							if (info.validKinds[j] != 0L)
								ostr.print("active" + j + ", 0x"
										+ Long.toHexString(info.validKinds[j])
										+ "L");
							else
								ostr.print("active" + j + ", 0L");
						}

						ostr.println(");");
					}
				} else {
					// A very special case.
					if (i == 0 && LexGen.mixed[LexGen.lexStateIndex]) {

						if (NfaState.generatedStates != 0)
							ostr.println("         return jjMoveNfa"
									+ LexGen.lexStateSuffix + "("
									+ NfaState.InitStateName() + ", 0);");
						else
							ostr.println("         return 1;");
					} else if (i != 0) // No more str literals to look for
					{
						ostr.println("         break;");
						startNfaNeeded = true;
					}
				}
			}

			/*
			 * default means that the current character is not in any of the
			 * strings at this position.
			 */
			ostr.println("      default :");

			if (Options.getDebugTokenManager())
				ostr
						.println("      debugStream.println(\"   No string literal matches possible.\");");

			if (NfaState.generatedStates != 0) {
				if (i == 0) {
					/*
					 * This means no string literal is possible. Just move nfa
					 * with this guy and return.
					 */
					ostr.println("         return jjMoveNfa"
							+ LexGen.lexStateSuffix + "("
							+ NfaState.InitStateName() + ", 0);");
				} else {
					ostr.println("         break;");
					startNfaNeeded = true;
				}
			} else {
				ostr.println("         return " + (i + 1) + ";");
			}

			ostr.println("   }");

			if (i != 0) {
				if (startNfaNeeded) {
					if (!LexGen.mixed[LexGen.lexStateIndex]
							&& NfaState.generatedStates != 0) {
						/*
						 * Here, a string literal is successfully matched and no
						 * more string literals are possible. So set the kind
						 * and state set upto and including this position for
						 * the matched string.
						 */

						ostr.print("   return jjStartNfa"
								+ LexGen.lexStateSuffix + "(" + (i - 1) + ", ");
						for (k = 0; k < maxLongsReqd - 1; k++)
							if (i <= maxLenForActive[k])
								ostr.print("active" + k + ", ");
							else
								ostr.print("0L, ");
						if (i <= maxLenForActive[k])
							ostr.println("active" + k + ");");
						else
							ostr.println("0L);");
					} else if (NfaState.generatedStates != 0)
						ostr.println("   return jjMoveNfa"
								+ LexGen.lexStateSuffix + "("
								+ NfaState.InitStateName() + ", " + i + ");");
					else
						ostr.println("   return " + (i + 1) + ";");
				}
			}

			ostr.println("}");
		}
	}

	static final int GetStrKind(String str) {
		for (int i = 0; i < maxStrKind; i++) {
			if (LexGen.lexStates[i] != LexGen.lexStateIndex)
				continue;

			String image = allImages[i];
			if (image != null && image.equals(str))
				return i;
		}

		return Integer.MAX_VALUE;
	}

	static void GenerateNfaStartStates(java.io.PrintWriter ostr,
			NfaState initialState) {
		boolean[] seen = new boolean[NfaState.generatedStates];
		Hashtable stateSets = new Hashtable();
		String stateSetString = "";
		int i, j, kind, jjmatchedPos = 0;
		int maxKindsReqd = maxStrKind / 64 + 1;
		long[] actives;
		Vector newStates = new Vector();
		Vector oldStates = null, jjtmpStates;

		statesForPos = new Hashtable[maxLen];
		intermediateKinds = new int[maxStrKind + 1][];
		intermediateMatchedPos = new int[maxStrKind + 1][];

		for (i = 0; i < maxStrKind; i++) {
			if (LexGen.lexStates[i] != LexGen.lexStateIndex)
				continue;

			String image = allImages[i];

			if (image == null || image.length() < 1)
				continue;

			try {
				if ((oldStates = (Vector) initialState.epsilonMoves.clone()) == null
						|| oldStates.size() == 0) {
					DumpNfaStartStatesCode(statesForPos, ostr);
					return;
				}
			} catch (Exception e) {
				JavaCCErrors.semantic_error("Error cloning state vector");
			}

			intermediateKinds[i] = new int[image.length()];
			intermediateMatchedPos[i] = new int[image.length()];
			jjmatchedPos = 0;
			kind = Integer.MAX_VALUE;

			for (j = 0; j < image.length(); j++) {
				if (oldStates == null || oldStates.size() <= 0) {
					// Here, j > 0
					kind = intermediateKinds[i][j] = intermediateKinds[i][j - 1];
					jjmatchedPos = intermediateMatchedPos[i][j] = intermediateMatchedPos[i][j - 1];
				} else {
					kind = NfaState.MoveFromSet(image.charAt(j), oldStates,
							newStates);
					oldStates.removeAllElements();

					if (j == 0
							&& kind != Integer.MAX_VALUE
							&& LexGen.canMatchAnyChar[LexGen.lexStateIndex] != -1
							&& kind > LexGen.canMatchAnyChar[LexGen.lexStateIndex])
						kind = LexGen.canMatchAnyChar[LexGen.lexStateIndex];

					if (GetStrKind(image.substring(0, j + 1)) < kind) {
						intermediateKinds[i][j] = kind = Integer.MAX_VALUE;
						jjmatchedPos = 0;
					} else if (kind != Integer.MAX_VALUE) {
						intermediateKinds[i][j] = kind;
						jjmatchedPos = intermediateMatchedPos[i][j] = j;
					} else if (j == 0)
						kind = intermediateKinds[i][j] = Integer.MAX_VALUE;
					else {
						kind = intermediateKinds[i][j] = intermediateKinds[i][j - 1];
						jjmatchedPos = intermediateMatchedPos[i][j] = intermediateMatchedPos[i][j - 1];
					}

					stateSetString = NfaState.GetStateSetString(newStates);
				}

				if (kind == Integer.MAX_VALUE
						&& (newStates == null || newStates.size() == 0))
					continue;

				int p;
				if (stateSets.get(stateSetString) == null) {
					stateSets.put(stateSetString, stateSetString);
					for (p = 0; p < newStates.size(); p++) {
						if (seen[((NfaState) newStates.elementAt(p)).stateName])
							((NfaState) newStates.elementAt(p)).inNextOf++;
						else
							seen[((NfaState) newStates.elementAt(p)).stateName] = true;
					}
				} else {
					for (p = 0; p < newStates.size(); p++)
						seen[((NfaState) newStates.elementAt(p)).stateName] = true;
				}

				jjtmpStates = oldStates;
				oldStates = newStates;
				(newStates = jjtmpStates).removeAllElements();

				if (statesForPos[j] == null)
					statesForPos[j] = new Hashtable();

				if ((actives = ((long[]) statesForPos[j].get(kind + ", "
						+ jjmatchedPos + ", " + stateSetString))) == null) {
					actives = new long[maxKindsReqd];
					statesForPos[j].put(kind + ", " + jjmatchedPos + ", "
							+ stateSetString, actives);
				}

				actives[i / 64] |= 1L << (i % 64);
				// String name = NfaState.StoreStateSet(stateSetString);
			}
		}

		DumpNfaStartStatesCode(statesForPos, ostr);
	}

	static void DumpNfaStartStatesCode(Hashtable[] statesForPos,
			java.io.PrintWriter ostr) {
		if (maxStrKind == 0) { // No need to generate this function
			return;
		}

		int i, maxKindsReqd = maxStrKind / 64 + 1;
		boolean condGenerated = false;
		int ind = 0;

		ostr.print("private" + (Options.getStatic() ? " static" : "")
				+ " final int jjStopStringLiteralDfa" + LexGen.lexStateSuffix
				+ "(int pos, ");
		for (i = 0; i < maxKindsReqd - 1; i++)
			ostr.print("long active" + i + ", ");
		ostr.println("long active" + i + ")\n{");

		if (Options.getDebugTokenManager())
			ostr
					.println("      debugStream.println(\"   No more string literal token matches are possible.\");");

		ostr.println("   switch (pos)\n   {");

		for (i = 0; i < maxLen - 1; i++) {
			if (statesForPos[i] == null)
				continue;

			ostr.println("      case " + i + ":");

			Enumeration e = statesForPos[i].keys();
			while (e.hasMoreElements()) {
				String stateSetString = (String) e.nextElement();
				long[] actives = (long[]) statesForPos[i].get(stateSetString);

				for (int j = 0; j < maxKindsReqd; j++) {
					if (actives[j] == 0L)
						continue;

					if (condGenerated)
						ostr.print(" || ");
					else
						ostr.print("         if (");

					condGenerated = true;

					ostr.print("(active" + j + " & 0x"
							+ Long.toHexString(actives[j]) + "L) != 0L");
				}

				if (condGenerated) {
					ostr.println(")");

					String kindStr = stateSetString.substring(0,
							ind = stateSetString.indexOf(", "));
					String afterKind = stateSetString.substring(ind + 2);
					int jjmatchedPos = Integer.parseInt(afterKind.substring(0,
							afterKind.indexOf(", ")));

					if (!kindStr.equals(String.valueOf(Integer.MAX_VALUE)))
						ostr.println("         {");

					if (!kindStr.equals(String.valueOf(Integer.MAX_VALUE))) {
						if (i == 0) {
							ostr.println("            jjmatchedKind = "
									+ kindStr + ";");

							if ((LexGen.initMatch[LexGen.lexStateIndex] != 0 && LexGen.initMatch[LexGen.lexStateIndex] != Integer.MAX_VALUE))
								ostr.println("            jjmatchedPos = 0;");
						} else if (i == jjmatchedPos) {
							if (subStringAtPos[i]) {
								ostr.println("            if (jjmatchedPos != "
										+ i + ")");
								ostr.println("            {");
								ostr.println("               jjmatchedKind = "
										+ kindStr + ";");
								ostr.println("               jjmatchedPos = "
										+ i + ";");
								ostr.println("            }");
							} else {
								ostr.println("            jjmatchedKind = "
										+ kindStr + ";");
								ostr.println("            jjmatchedPos = " + i
										+ ";");
							}
						} else {
							if (jjmatchedPos > 0)
								ostr.println("            if (jjmatchedPos < "
										+ jjmatchedPos + ")");
							else
								ostr
										.println("            if (jjmatchedPos == 0)");
							ostr.println("            {");
							ostr.println("               jjmatchedKind = "
									+ kindStr + ";");
							ostr.println("               jjmatchedPos = "
									+ jjmatchedPos + ";");
							ostr.println("            }");
						}
					}

					kindStr = stateSetString.substring(0, ind = stateSetString
							.indexOf(", "));
					afterKind = stateSetString.substring(ind + 2);
					stateSetString = afterKind.substring(afterKind
							.indexOf(", ") + 2);

					if (stateSetString.equals("null;"))
						ostr.println("            return -1;");
					else
						ostr.println("            return "
								+ NfaState.AddStartStateSet(stateSetString)
								+ ";");

					if (!kindStr.equals(String.valueOf(Integer.MAX_VALUE)))
						ostr.println("         }");
					condGenerated = false;
				}
			}

			ostr.println("         return -1;");
		}

		ostr.println("      default :");
		ostr.println("         return -1;");
		ostr.println("   }");
		ostr.println("}");

		ostr.print("private" + (Options.getStatic() ? " static" : "")
				+ " final int jjStartNfa" + LexGen.lexStateSuffix
				+ "(int pos, ");
		for (i = 0; i < maxKindsReqd - 1; i++)
			ostr.print("long active" + i + ", ");
		ostr.println("long active" + i + ")\n{");

		if (LexGen.mixed[LexGen.lexStateIndex]) {
			if (NfaState.generatedStates != 0)
				ostr.println("   return jjMoveNfa" + LexGen.lexStateSuffix
						+ "(" + NfaState.InitStateName() + ", pos + 1);");
			else
				ostr.println("   return pos + 1;");

			ostr.println("}");
			return;
		}

		ostr.print("   return jjMoveNfa" + LexGen.lexStateSuffix + "("
				+ "jjStopStringLiteralDfa" + LexGen.lexStateSuffix + "(pos, ");
		for (i = 0; i < maxKindsReqd - 1; i++)
			ostr.print("active" + i + ", ");
		ostr.print("active" + i + ")");
		ostr.println(", pos + 1);");
		ostr.println("}");
	}

	public static void reInit() {
		maxStrKind = 0;
		maxLen = 0;
		charCnt = 0;
		charPosKind = new Vector();
		maxLenForActive = new int[100];
		allImages = null;
		intermediateKinds = null;
		intermediateMatchedPos = null;
		startStateCnt = 0;
		subString = null;
		subStringAtPos = null;
		statesForPos = null;
		boilerPlateDumped = false;
	}

}
